package com.basker.pisces.springcloud.feign.fallback;

import java.lang.reflect.Proxy;

import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import feign.hystrix.FallbackFactory;

/**
 * 默认的FallbackFactory基类，通过JDK动态代理生成fallback实例
 *
 * @author hangwen
 */
public abstract class ProxyFallbackFactory implements FallbackFactory<Object> {

    /**
     * 创建clientType的jdk动态代理,指定自定义的fallback处理器
     *
     * @param clientType
     * @param invocationHandler
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T create(Class<T> clientType, FallbackInvocationHandler invocationHandler) {
        Assert.notNull(clientType, "parameter 'clientType' is required");
        Assert.isTrue(clientType.isInterface(), "parameter 'clientType' must be a interface!");

        Assert.notNull(invocationHandler, "parameter 'invocationHandler' is required");
        Assert.notNull(invocationHandler.getCause(), "parameter 'invocationHandler.getCause()' is required");

        return (T) Proxy.newProxyInstance(clientType.getClassLoader(), new Class[]{clientType}, invocationHandler);

    }

    /**
     * 创建clientType的jdk动态代理，提供默认的fallback处理器
     *
     * @param clientType
     * @param cause
     * @return
     */
    public static <T> T create(Class<T> clientType, Throwable cause) {
        Assert.notNull(clientType, "parameter 'clientType' is required");
        Assert.notNull(cause, "parameter 'cause' is required");

        return create(clientType, new FallbackInvocationHandler(clientType, cause));
    }

    @Override
    public Object create(Throwable cause) {
        try {
            String clientClassName = getClientClassName();
            Class<?> clientType = ClassUtils.forName(clientClassName, null);
            return create(clientType, cause);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("fallback class:'" + getClientClassName() + "' is not found");
        }

    }

    /**
     * 用于确定当前工厂创建的FeignClient实例类型
     *
     * @return
     */
    protected abstract String getClientClassName();

}
